package net;
import java.io.File;
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket; 
import java.net.SocketException;
import java.net.UnknownHostException;

import ui.server.*;
import util.IEventCallback;
import util.IOUtil;
import util.MessageInfo;

public class ClientThread implements Runnable , IEventCallback
{
	class FileInfo {
		String filepath;
		String filename;
		long   filesize;
		FileInfo() {
			filepath = "";
			filename = "";
			filesize = 0;
		};
		FileInfo(String filepath) {
			File srcFile = new File(filepath);
			this.filepath = srcFile.getAbsolutePath();
		    this.filename = srcFile.getName();
		    this.filesize = srcFile.length();
		    dumpFileInfo();
		}
		
		public void parseFileInfo(String filepath, String delimiter) {
			int pos_size = filepath.lastIndexOf(delimiter) + 1;
	    	this.filesize = Long.valueOf(filepath.substring(pos_size)).longValue();
	    	this.filepath = filepath.substring(0, pos_size-1);
	    	File srcFile = new File(this.filepath);
	    	this.filename = srcFile.getName();
	    	dumpFileInfo();
		}
		public void dumpFileInfo()
		{
			System.out.println("filepath:" + filepath);
			System.out.println("filename:" + filename);
			System.out.println("filesize:" + filesize);
		}
	}
	private ClientSocket cs = null;
	private FileTransferServer server = null;
	private volatile boolean stopRequested;
	
	
	public ClientThread(Socket s, FileTransferServer fileTransferServer) {
		// TODO Auto-generated constructor stub
		this.cs = new ClientSocket(s, s.getInetAddress().toString(), s.getPort());
		this.server = fileTransferServer;
		System.out.println("accept this client connection....."); 
	}

	public void run() {
		System.out.println("[ClientThread] start client thread"); 
        try {
        	
        	// Get target file length       	
        	FileInfo srcInfo = this.getSourceFileInfo(IOUtil.DEFAULT_DELIMITER);
        	FileInfo dstInfo = this.getTargetFileInfo(srcInfo.filename);

        	// Send target info message
        	String strInfo = dstInfo.filename + IOUtil.DEFAULT_DELIMITER + dstInfo.filesize;
        	System.out.println("[ClientThread] Send target info message: " + strInfo);
    		cs.sendMessage(strInfo);
    		
    		// Get user comamnd
    		String command = cs.getMessage();
        	if(!command.equalsIgnoreCase(IOUtil.CMD_CANCEL))
        	{
        		boolean isResume = command.equalsIgnoreCase(IOUtil.CMD_RESUME) ? true : false;
        		long dstFileSize = isResume ? dstInfo.filesize : 0;
        		InputStream inStream = cs.getInputStream();
            	
        		OutputStream outStream = new FileOutputStream(dstInfo.filepath, isResume);
        		this.sendServerMsg(IEventCallback.EVT_SERVER_RECV_FILE_START);
        		System.out.println("[ClientThread] receive file start! "); 
            	try {
        			byte[] buffer = new byte[IOUtil.DEFAULT_READ_BUFFER_SIZE];
        			long currentWriteBytes = dstFileSize;
        			int bytesRead = -1;
        			int preProgress = 0;

        			while ( (bytesRead = inStream.read(buffer)) > -1 ) {
        				if(stopRequested)
        					break;
        				currentWriteBytes += bytesRead;
        				outStream.write(buffer, 0, bytesRead);
        				int progress = IOUtil.getProgress(currentWriteBytes, srcInfo.filesize);
        				//System.out.println("[ClientThread] currentWriteBytes: " + currentWriteBytes);
        				if(progress != preProgress)
        				{
        					this.sendServerMsg(IEventCallback.EVT_SERVER_RECV_FILE_PROGRESS, String.valueOf(progress));
        					preProgress = progress;
        				}
        				if(srcInfo.filesize == currentWriteBytes)
    						break;
        			}
        			this.sendServerMsg(IEventCallback.EVT_SERVER_RECV_FILE_COMPLETE);
        			System.out.println("GET IEventCallback.EVT_CLIENT_CLOSE_READY start");
                	cs.getMessageInfo();   
                	System.out.println("SEND IEventCallback.EVT_CLIENT_CLOSE_READY pass");
                	
        		} catch (UnknownHostException e) {
        			System.out.println(e.getMessage().toString());
        			this.callback(e.getMessage(), IEventCallback.ERR_UNKNOWN_HOST_EXCEPTION);
        		} catch (SocketException e) {
        			System.out.println(e.getMessage().toString());
        			this.callback(e.getMessage(), IEventCallback.ERR_SOCKET_EXCEPTION);
        		} catch (IOException e) {
        			System.out.println(e.getMessage().toString());
        			this.callback(e.getMessage(), IEventCallback.ERR_IO_EXCEPTION);
        		} 
        		finally {
        			//inStream.close();
                	outStream.close();
                	System.out.println("[ClientThread] copystream end");
        		}
        	} 
    	} catch (IOException e) {
    		this.callback(IEventCallback.ERR_IO_EXCEPTION);
    		e.printStackTrace();    		
    	} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				cs.closeConnection();
			} catch (Exception e) {
				e.printStackTrace();
			}
			System.out.println("[ClientThread] connection close, client exit......");
		}
	}

	public void callback(long... argv) {
		server.callback(argv);
	} 

	public void callback(String message, long... argv) {
		server.callback(message, argv);
	}
	
	private FileInfo getSourceFileInfo(String delimiter) throws Exception {
		FileInfo info = new FileInfo();
		try {
			String sourcefile = cs.getMessage();
			info.parseFileInfo(sourcefile, delimiter);
	    	
		} catch (IOException e) {
			e.printStackTrace();
			throw e;
		}
		return info;
	}
	
	public void stopRequest() throws Exception {
		System.out.println("[FileTransferServer] stopRequest");
		stopRequested = true;
		//this.callback(IEventCallback.EVT_SERVER_SEND_FILE_STOP);
	}
	
	
	private FileInfo getTargetFileInfo(String filename) throws Exception {
		FileInfo info;
		try {
			
			String basedir = server.getTargetDir()+ "\\";
			String filepath = basedir+filename;
			info = new FileInfo(filepath);
			
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
		return info;
	}
	
	private void sendServerMsg(long event) throws Exception
	{
		if(!cs.isNullSocket())
		{
			MessageInfo msgInfo = new MessageInfo(event);
			cs.sendMessageInfo(msgInfo);
			this.callback(event);
		}
	}
	 
	private void sendServerMsg(long event, String msg) throws Exception
	{
		if(!cs.isNullSocket())
		{
			MessageInfo msgInfo = new MessageInfo(event, msg);
			cs.sendMessageInfo(msgInfo);
			this.callback(event, Long.valueOf(msg).longValue());
		}
	}
}
